// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Global

/* PdfExteGState.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System.Globalization;

#endregion

#nullable enable

namespace PdfSharpCore.Pdf.Advanced;

/// <summary>
/// Represents an extended graphics state object.
/// </summary>
public sealed class PdfExtGState
    : PdfDictionary
{
    #region Construction

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfExtGState"/> class.
    /// </summary>
    /// <param name="document">The document.</param>
    public PdfExtGState (PdfDocument document)
        : base (document)
    {
        Elements.SetName (Keys.Type, "/ExtGState");

#if true_
            //AIS false
            //BM /Normal
            //ca 1
            //CA 1
            //op false
            //OP false
            //OPM 1
            //SA true
            //SMask /None
            //Type /ExtGState

            Elements.SetValue(Keys.AIS, new PdfBoolean(false)); // The alpha source
            Elements.SetName("/BM", "Normal");
            Elements.SetValue(Keys.op, new PdfBoolean(false));
            Elements.SetValue(Keys.OP, new PdfBoolean(false));
            Elements.SetValue(Keys.OPM, new PdfInteger(1));
            Elements.SetValue("/SA", new PdfBoolean(true));
            Elements.SetName("/SMask", "None");
#endif

        //#if OP_HACK
        //            Elements.SetValue(Keys.op, new PdfBoolean(false));
        //            Elements.SetValue(Keys.OP, new PdfBoolean(false));
        //            Elements.SetValue(Keys.OPM, new PdfInteger(1));
        //#endif
    }

    #endregion

    /// <summary>
    /// Used in Edf.Xps.
    /// </summary>
    internal void SetDefault1()
    {
        //<<
        //  /AIS false
        //  /BM /Normal
        //  /ca 1
        //  /CA 1
        //  /op false
        //  /OP false
        //  /OPM 1
        //  /SA true
        //  /SMask /None
        //  /Type /ExtGState
        //>>
        Elements.SetBoolean (Keys.AIS, false);
        if (Elements.ContainsKey (Keys.BM)) Elements.SetName (Keys.BM, "/Normal");
        StrokeAlpha = 1;
        NonStrokeAlpha = 1;
        Elements.SetBoolean (Keys.op, false);
        Elements.SetBoolean (Keys.OP, false);
        Elements.SetBoolean (Keys.SA, true);
        Elements.SetName (Keys.SMask, "/None");
    }

    /// <summary>
    /// Used in Edf.Xps.
    /// ...for shading patterns
    /// </summary>
    internal void SetDefault2()
    {
        //<<
        //  /AIS false
        //  /BM /Normal
        //  /ca 1
        //  /CA 1
        //  /op true
        //  /OP true
        //  /OPM 1
        //  /SA true
        //  /SMask /None
        //  /Type /ExtGState
        //>>
        Elements.SetBoolean (Keys.AIS, false);
        Elements.SetName (Keys.BM, "/Normal");
        StrokeAlpha = 1;
        NonStrokeAlpha = 1;
        Elements.SetBoolean (Keys.op, true);
        Elements.SetBoolean (Keys.OP, true);
        Elements.SetInteger (Keys.OPM, 1);
        Elements.SetBoolean (Keys.SA, true);
        Elements.SetName (Keys.SMask, "/None");
    }

    /// <summary>
    /// Sets the alpha value for stroking operations.
    /// </summary>
    public double StrokeAlpha
    {
        set
        {
            _strokeAlpha = value;
            Elements.SetReal (Keys.CA, value);
            UpdateKey();
        }
    }

    double _strokeAlpha;

    /// <summary>
    /// Sets the alpha value for nonstroking operations.
    /// </summary>
    public double NonStrokeAlpha
    {
        set
        {
            _nonStrokeAlpha = value;
            Elements.SetReal (Keys.ca, value);
            UpdateKey();
        }
    }

    double _nonStrokeAlpha;

    /// <summary>
    /// Sets the overprint value for stroking operations.
    /// </summary>
    public bool StrokeOverprint
    {
        set
        {
            _strokeOverprint = value;
            Elements.SetBoolean (Keys.OP, value);
            UpdateKey();
        }
    }

    bool _strokeOverprint;

    /// <summary>
    /// Sets the overprint value for nonstroking operations.
    /// </summary>
    public bool NonStrokeOverprint
    {
        set
        {
            _nonStrokeOverprint = value;
            Elements.SetBoolean (Keys.op, value);
            UpdateKey();
        }
    }

    bool _nonStrokeOverprint;

    /// <summary>
    /// Sets a soft mask object.
    /// </summary>
    public PdfSoftMask SoftMask
    {
        set => Elements.SetReference (Keys.SMask, value);
    }

    internal string? Key => _key;

    private void UpdateKey()
    {
        _key = ((int)(1000 * _strokeAlpha)).ToString (CultureInfo.InvariantCulture) +
               ((int)(1000 * _nonStrokeAlpha)).ToString (CultureInfo.InvariantCulture) +
               (_strokeOverprint ? "S" : "s") + (_nonStrokeOverprint ? "N" : "n");
    }

    private string? _key;

    internal static string MakeKey (double alpha, bool overPaint)
    {
        var key = ((int)(1000 * alpha)).ToString (CultureInfo.InvariantCulture) + (overPaint ? "O" : "0");
        return key;
    }

    /// <summary>
    /// Common keys for all streams.
    /// </summary>
    internal sealed class Keys : KeysBase
    {
        // ReSharper disable InconsistentNaming

        /// <summary>
        /// (Optional) The type of PDF object that this dictionary describes;
        /// must be ExtGState for a graphics state parameter dictionary.
        /// </summary>
        [KeyInfo (KeyType.Name | KeyType.Optional)]
        public const string Type = "/Type";

        /// <summary>
        /// (Optional; PDF 1.3) The line width (see �Line Width� on page 185).
        /// </summary>
        [KeyInfo (KeyType.Real | KeyType.Optional)]
        public const string LW = "/LW";

        /// <summary>
        /// (Optional; PDF 1.3) The line cap style.
        /// </summary>
        [KeyInfo (KeyType.Integer | KeyType.Optional)]
        public const string LC = "/LC";

        /// <summary>
        /// (Optional; PDF 1.3) The line join style.
        /// </summary>
        [KeyInfo (KeyType.Integer | KeyType.Optional)]
        public const string LJ = "/LJ";

        /// <summary>
        /// (Optional; PDF 1.3) The miter limit.
        /// </summary>
        [KeyInfo (KeyType.Real | KeyType.Optional)]
        public const string ML = "/ML";

        /// <summary>
        /// (Optional; PDF 1.3) The line dash pattern, expressed as an array of the form
        /// [dashArray dashPhase], where dashArray is itself an array and dashPhase is an integer.
        /// </summary>
        [KeyInfo (KeyType.Array | KeyType.Optional)]
        public const string D = "/D";

        /// <summary>
        /// (Optional; PDF 1.3) The name of the rendering intent.
        /// </summary>
        [KeyInfo (KeyType.Name | KeyType.Optional)]
        public const string RI = "/RI";

        /// <summary>
        /// (Optional) A flag specifying whether to apply overprint. In PDF 1.2 and earlier,
        /// there is a single overprint parameter that applies to all painting operations.
        /// Beginning with PDF 1.3, there are two separate overprint parameters: one for stroking
        /// and one for all other painting operations. Specifying an OP entry sets both parameters
        /// unless there is also an op entry in the same graphics state parameter dictionary, in
        /// which case the OP entry sets only the overprint parameter for stroking.
        /// </summary>
        [KeyInfo (KeyType.Boolean | KeyType.Optional)]
        public const string OP = "/OP";

        /// <summary>
        /// (Optional; PDF 1.3) A flag specifying whether to apply overprint for painting operations
        /// other than stroking. If this entry is absent, the OP entry, if any, sets this parameter.
        /// </summary>
        [KeyInfo (KeyType.Boolean | KeyType.Optional)]
        public const string op = "/op";

        /// <summary>
        /// (Optional; PDF 1.3) The overprint mode.
        /// </summary>
        [KeyInfo (KeyType.Integer | KeyType.Optional)]
        public const string OPM = "/OPM";

        /// <summary>
        /// (Optional; PDF 1.3) An array of the form [font size], where font is an indirect
        /// reference to a font dictionary and size is a number expressed in text space units.
        /// These two objects correspond to the operands of the Tf operator; however,
        /// the first operand is an indirect object reference instead of a resource name.
        /// </summary>
        [KeyInfo (KeyType.Array | KeyType.Optional)]
        public const string Font = "/Font";

        /// <summary>
        /// (Optional) The black-generation function, which maps the interval [0.0 1.0]
        /// to the interval [0.0 1.0].
        /// </summary>
        [KeyInfo (KeyType.Function | KeyType.Optional)]
        public const string BG = "/BG";

        /// <summary>
        /// (Optional; PDF 1.3) Same as BG except that the value may also be the name Default,
        /// denoting the black-generation function that was in effect at the start of the page.
        /// If both BG and BG2 are present in the same graphics state parameter dictionary,
        /// BG2 takes precedence.
        /// </summary>
        [KeyInfo (KeyType.FunctionOrName | KeyType.Optional)]
        public const string BG2 = "/BG2";

        /// <summary>
        /// (Optional) The undercolor-removal function, which maps the interval
        /// [0.0 1.0] to the interval [-1.0 1.0].
        /// </summary>
        [KeyInfo (KeyType.Function | KeyType.Optional)]
        public const string UCR = "/UCR";

        /// <summary>
        /// (Optional; PDF 1.3) Same as UCR except that the value may also be the name Default,
        /// denoting the undercolor-removal function that was in effect at the start of the page.
        /// If both UCR and UCR2 are present in the same graphics state parameter dictionary,
        /// UCR2 takes precedence.
        /// </summary>
        [KeyInfo (KeyType.FunctionOrName | KeyType.Optional)]
        public const string UCR2 = "/UCR2";

        //TR  function, array, or name
        //TR2 function, array, or name
        //HT  dictionary, stream, or name
        //FL  number
        //SM  number

        /// <summary>
        /// (Optional) A flag specifying whether to apply automatic stroke adjustment.
        /// </summary>
        [KeyInfo (KeyType.Boolean | KeyType.Optional)]
        public const string SA = "/SA";

        /// <summary>
        /// (Optional; PDF 1.4) The current blend mode to be used in the transparent imaging model.
        /// </summary>
        [KeyInfo (KeyType.NameOrArray | KeyType.Optional)]
        public const string BM = "/BM";

        /// <summary>
        /// (Optional; PDF 1.4) The current soft mask, specifying the mask shape or
        /// mask opacity values to be used in the transparent imaging model.
        /// </summary>
        [KeyInfo (KeyType.NameOrDictionary | KeyType.Optional)]
        public const string SMask = "/SMask";

        /// <summary>
        /// (Optional; PDF 1.4) The current stroking alpha constant, specifying the constant
        /// shape or constant opacity value to be used for stroking operations in the transparent
        /// imaging model.
        /// </summary>
        [KeyInfo (KeyType.Real | KeyType.Optional)]
        public const string CA = "/CA";

        /// <summary>
        /// (Optional; PDF 1.4) Same as CA, but for nonstroking operations.
        /// </summary>
        [KeyInfo (KeyType.Real | KeyType.Optional)]
        public const string ca = "/ca";

        /// <summary>
        /// (Optional; PDF 1.4) The alpha source flag (�alpha is shape�), specifying whether
        /// the current soft mask and alpha constant are to be interpreted as shape values (true)
        /// or opacity values (false).
        /// </summary>
        [KeyInfo (KeyType.Boolean | KeyType.Optional)]
        public const string AIS = "/AIS";

        /// <summary>
        /// (Optional; PDF 1.4) The text knockout flag, which determines the behavior of
        /// overlapping glyphs within a text object in the transparent imaging model.
        /// </summary>
        [KeyInfo (KeyType.Boolean | KeyType.Optional)]
        public const string TK = "/TK";

        // ReSharper restore InconsistentNaming

        /// <summary>
        /// Gets the KeysMeta for these keys.
        /// </summary>
        internal static DictionaryMeta Meta
        {
            get { return _meta ??= CreateMeta (typeof (Keys)); }
        }

        static DictionaryMeta? _meta;
    }

    /// <summary>
    /// Gets the KeysMeta of this dictionary type.
    /// </summary>
    internal override DictionaryMeta Meta => Keys.Meta;
}
